<template>
  <div class="box">
    <canvas id="canvas"></canvas>
  </div>
</template>

<script>
export default {
  data() {
    return {
      canvas: "",
      ctx: "",
      startRadian: 0, // 初始角度
      canBeClick: true, // 控制抽奖进行中不让再抽奖
      index: "",
      awards: [], // 奖品
      currIndex: "",
      value: "0",
      drawList: "",
    }
  },
  props: {
    rate: { type: Number, default: 100 }, // 转盘速率
    radius: { type: Number, default: 150 }, // 转盘半径
    textFontSize: { type: String, default: "13px" }, // 文字大小
    lineHeight: { type: Number, default: 20 }, // 文字行高
    textColor: { type: String, default: "#d64737" }, // 文字颜色
    textMargin: { type: Number, default: 30 }, // 文字距离边框距离
    textPadding: { type: Number, default: 0 }, // 文字补偿宽度
    btnFontSize: { type: String, default: "26px" }, // 按钮文字大小
    btnColor: { type: String, default: "#d64737" }, // 按钮文件颜色
    btnBorderColor1: { type: String, default: "#d64737" }, // 按钮外边框颜色
    btnBorderColor2: { type: String, default: "#ffffff" }, // 按钮内边框颜色
    btnBorderColor3: { type: String, default: "#FFF951" }, // 按钮指针颜色
    btnBgColor: { type: String, default: "#ffdea0" }, // 按钮背景颜色
    btnText: { type: String, default: "抽奖" }, // 按钮内容
    btnRadius: { type: Number, default: 45 }, // 按钮半径
    borderColor: { type: String, default: "#FF6969" }, // 边框颜色
  },
  mounted() {
    // this.getlist()
    this.awards = [
      // 奖品
      { name: "价值5988元华为 P30pro", color: "#f9e3bb" },
      { name: "价值398元车载空气净化器", color: "#f8d384" },
      { name: "价值25元百叶帘遮阳挡", color: "#f9e3bb" },
      { name: "16元油卡套餐红包", color: "#f8d384" },
      { name: "5元油卡直冲红包", color: "#f9e3bb" },
      { name: "3元话费直冲红包", color: "#f8d384" },
      { name: "价值32元重力感应手机支架", color: "#f9e3bb" },
      { name: "价值198元手提迷你车在保温冷藏箱", color: "#f8d384" },
    ]
    this.initCanvas()
  },
  methods: {
    getlist() {
      this.$post({
        url: "/Home/Index/lottoInfo",
        success: (res) => {
          console.log(res)
        },
      })
    },
    /**
     * 初始化转盘
     */
    initCanvas() {
      this.canvas = document.querySelector("#canvas")
      this.ctx = canvas.getContext("2d")
      this.canvas.width = this.radius * 2
      this.canvas.height = this.radius * 2
      this.render()
      this.startRotate()
    },
    /**
     * 处理文字换行
     */
    getLineTextList(ctx, text, maxLineWidth) {
      maxLineWidth += this.textPadding
      let wordList = text.split(""),
        tempLine = "",
        lineList = []
      for (let i = 0; i < wordList.length; i++) {
        if (ctx.measureText(tempLine).width >= maxLineWidth) {
          lineList.push(tempLine)
          maxLineWidth -= ctx.measureText(text[0]).width
          tempLine = ""
        }
        tempLine += wordList[i]
      }
      lineList.push(tempLine)
      return lineList
    },

    /**
     * 绘制转盘
     */
    drawPanel() {
      const ctx = this.ctx
      const startRadian = this.startRadian
      ctx.save()
      ctx.beginPath()
      ctx.fillStyle = this.borderColor
      // 根据我们设定的初始角度来绘制转盘
      ctx.arc(
        this.radius,
        this.radius,
        this.radius,
        startRadian,
        Math.PI * 2 + startRadian,
        false
      )
      ctx.fill()
      ctx.restore()
    },

    /**
     * 绘制奖品
     */
    drawPrizeBlock() {
      const ctx = this.ctx
      const awards = this.awards
      // 根据初始角度来绘制奖品块
      let startRadian = this.startRadian
      let RadianGap = (Math.PI * 2) / awards.length
      let endRadian = startRadian + RadianGap
      for (let i = 0; i < awards.length; i++) {
        ctx.save()
        ctx.beginPath()
        ctx.fillStyle = awards[i].color || (i % 2 == 0 ? "#7021E8" : "#E5DAF6")
        ctx.moveTo(this.radius, this.radius)
        // 外边框粗细
        ctx.arc(
          this.radius,
          this.radius,
          this.radius - 14,
          startRadian,
          endRadian,
          false
        )
        ctx.fill()
        ctx.restore()
        ctx.save()
        ctx.fillStyle = this.textColor
        ctx.font = `${this.textFontSize} Arial`
        ctx.translate(
          this.radius +
            Math.cos(startRadian + RadianGap / 2) *
              (this.radius - this.textMargin),
          this.radius +
            Math.sin(startRadian + RadianGap / 2) *
              (this.radius - this.textMargin)
        )
        ctx.rotate(startRadian + RadianGap / 2 + Math.PI / 2)
        this.getLineTextList(ctx, awards[i].name, 70).forEach((line, index) => {
          ctx.fillText(
            line,
            -ctx.measureText(line).width / 2,
            ++index * this.lineHeight
          )
        })
        ctx.restore()

        startRadian += RadianGap
        endRadian += RadianGap
      }
    },

    // 将canvas再window中的坐标点转化为canvas中的坐标点
    windowToCanvas(canvas, e) {
      // getBoundingClientRect这个方法返回html元素的大小及其相对于视口的位置
      const canvasPostion = canvas.getBoundingClientRect(),
        x = e.clientX,
        y = e.clientY
      return {
        x: x - canvasPostion.left,
        y: y - canvasPostion.top,
      }
    },
    // 初始化
    startRotate() {
      const canvas = this.canvas
      const ctx = this.ctx
      const canvasStyle = canvas.getAttribute("style")
      this.render()
      canvas.addEventListener("mousedown", (e) => {
        this.toDraw(e)
      })
      canvas.addEventListener("mousemove", (e) => {
        let loc = this.windowToCanvas(canvas, e)
        ctx.beginPath()
        ctx.arc(this.radius, this.radius, 30, 0, Math.PI * 2, false)
        if (ctx.isPointInPath(loc.x, loc.y)) {
          canvas.setAttribute("style", `cursor: pointer;${canvasStyle}`)
        } else {
          canvas.setAttribute("style", canvasStyle)
        }
      })
    },
    toDraw(e) {
      const canvas = this.canvas
      const ctx = this.ctx
      // 只要抽奖没有结束，就不让再次抽奖
      if (this.index < 0 || this.index >= this.awards.length)
        console.error("该索引的奖品不存在!")
      if (
        !this.canBeClick ||
        this.index < 0 ||
        this.index >= this.awards.length
      )
        return false
      this.canBeClick = false
      let loc = this.windowToCanvas(canvas, e)
      ctx.beginPath()
      ctx.arc(this.radius, this.radius, 50, 0, Math.PI * 2, false)
      if (ctx.isPointInPath(loc.x, loc.y)) {
        this.startRadian = -Math.floor(Math.random() * 180)
        // 每次点击抽奖，都将初始化角度重置
        // this.$post({
        //   url: "/Home/Index/lotto",
        //   success: (res) => {
        //     console.log("11111", res.data.code)
        //     if (res.data.code == 0) {
        //       this.drawList = res.data.data.name
        //       this.awards.forEach((v, index) => {
        //         if (v.id === res.data.data.id) {
        //           console.log("111", v.id)

        //           this.index = index
        //           console.log(this.index)
        //         }
        //       })
        //       // distance是计算出的将指定奖品旋转到指针处需要旋转的角度距离，distanceToStop下面会又说明
        //       const distance = this.distanceToStop()
        //       this.rotatePanel(distance)
        //       this.getlist()
        //     } else {
        //       this.$toast(res.data.msg)
        //       console.log("this.drawList")
        //     }
        //   },
        // })
        this.drawList = "恭喜抽中价值5988元华为 P30pro"
        this.awards.forEach((v, index) => {
          //   if (v.id === res.data.data.id) {
          //     this.index = index
          //     console.log(this.index)
          //   }
          this.index = 0
        })
        // distance是计算出的将指定奖品旋转到指针处需要旋转的角度距离，distanceToStop下面会又说明
        const distance = this.distanceToStop()
        this.rotatePanel(distance)
      } else {
        this.canBeClick = true
      }
    },

    // 处理旋转的关键方法
    rotatePanel(distance) {
      // 这里用一个很简单的缓动函数来计算每次绘制需要改变的角度，这样可以达到一个转盘从块到慢的渐变的过程
      let changeRadian = (distance - this.startRadian) / this.rate
      this.startRadian += changeRadian
      // 当最后的目标距离与startRadian之间的差距低于0.05时，就默认奖品抽完了，可以继续抽下一个了。
      if (distance - this.startRadian <= 0.05) {
        //抽奖结束
        this.$dialog
          .alert({
            title: "提示",
            message: this.drawList,
          })
          .then(() => {
            // on close
          })
        return (this.canBeClick = true)
      }
      this.render()
      window.requestAnimationFrame(this.rotatePanel.bind(this, distance))
    },

    // 绘制按钮，以及按钮上start的文字
    drawButton() {
      const ctx = this.ctx
      ctx.save()
      ctx.beginPath()
      ctx.fillStyle = this.btnBorderColor1
      // 按钮外环
      ctx.arc(this.radius, this.radius, this.btnRadius, 0, Math.PI * 2, false)
      ctx.fill()
      ctx.restore()

      ctx.save()
      ctx.beginPath()
      ctx.fillStyle = this.btnBorderColor2
      ctx.arc(
        this.radius,
        this.radius,
        this.btnRadius - 5,
        0,
        Math.PI * 2,
        false
      )
      ctx.fill()
      ctx.restore()

      ctx.save()
      ctx.beginPath()
      ctx.fillStyle = this.btnBorderColor3
      // 按钮内环，宽度
      ctx.arc(
        this.radius,
        this.radius,
        this.btnRadius - 6,
        0,
        Math.PI * 2,
        false
      )

      ctx.fill()
      ctx.restore()

      ctx.save()
      ctx.beginPath()
      ctx.fillStyle = this.btnBorderColor3
      // 按钮箭头左宽
      ctx.moveTo(
        this.radius - this.btnRadius / 3,
        this.radius - this.btnRadius / 2 - 5
      )
      // 按钮箭头高度
      ctx.lineTo(this.radius, this.radius - this.btnRadius - 20)
      // 按钮箭头右宽
      ctx.lineTo(
        this.radius + this.btnRadius / 3,
        this.radius - this.btnRadius / 2 - 5
      )
      ctx.closePath()
      ctx.fill()
      ctx.restore()

      ctx.save()
      ctx.beginPath()
      ctx.fillStyle = this.btnBgColor
      // 按钮背景大小
      ctx.arc(
        this.radius,
        this.radius,
        this.btnRadius - 15,
        0,
        Math.PI * 2,
        false
      )
      ctx.fill()
      ctx.restore()

      ctx.save()
      ctx.beginPath()
      ctx.fillStyle = this.btnColor
      ctx.font = `${this.btnFontSize} Arial`
      ctx.translate(this.radius, this.radius)
      ctx.fillText(this.btnText, -ctx.measureText(this.btnText).width / 2, 8)
      ctx.restore()
    },

    distanceToStop() {
      // middleDegrees为奖品块的中间角度（最终停留都是以中间角度进行计算的）距离初始的startRadian的距离，distance就是当前奖品跑到指针位置要转动的距离。
      let middleDegrees = 0,
        distance = 0
      // 映射出每个奖品的middleDegrees
      const awardsToDegreesList = this.awards.map((data, index) => {
        let awardRadian = (Math.PI * 2) / this.awards.length
        return (
          awardRadian * index +
          (awardRadian * (index + 1) - awardRadian * index) / 2
        )
      })
      // 此次抽奖应该中的奖品
      const currentPrizeIndex = this.index
      middleDegrees = awardsToDegreesList[currentPrizeIndex]
      // 因为指针是垂直向上的，相当坐标系的Math.PI/2,所以这里要进行判断来移动角度
      distance = (Math.PI * 3) / 2 - middleDegrees
      distance = distance > 0 ? distance : Math.PI * 2 + distance
      // 这里额外加上后面的值，是为了让转盘多转动几圈，看上去更像是在抽奖
      return distance + Math.PI * 10
    },

    render() {
      /**
       * 绘制转盘
       */
      this.drawPanel()
      /**
       * 绘制奖品
       */
      this.drawPrizeBlock()
      // 绘制按钮，以及按钮上start的文字
      this.drawButton()
    },
  },
}
</script>

<style scoped lang="scss">
#canvas {
  width: 100%;
  height: 100%;
}
</style>
